﻿
namespace ZDS

open System
open System.Collections
open System.Collections.Generic

[<AllowNullLiteral>]
type ZLnkLstNode<'t>(value : 't) =
    let mutable m_value = value
    let mutable m_next : ZLnkLstNode<'t> = null

    member this.Value 
        with get() = m_value
        and set newvalue = m_value <- newvalue

    member this.Next 
        with get() = m_next
        and set newvalue = m_next <- newvalue

type ZLinkList<'t>() = 

    let mutable m_size = 0
    // preHead doesn't contain the first element
    // it is the position before the first element
    let m_preHead = new ZLnkLstNode<'t>(Unchecked.defaultof<'t>)
    let mutable m_rear = m_preHead

    let nth_node n = 
        if n = -1 then
            m_preHead
        elif n = (m_size - 1) then
            m_rear
        else
            let mutable cursor : ZLnkLstNode<'t> = m_preHead
            for index = 1 to (n+1) do
                cursor <- cursor.Next
            cursor

    member this.Size = m_size
    member this.PreHead = m_preHead
    member this.Rear = m_rear

    member this.Append value = 
        m_size <- m_size + 1

        let newnode = new ZLnkLstNode<'t>(value)
        m_rear.Next <- newnode
        m_rear <- newnode

    member this.Insert index value =
        if index >= 0 && index <= m_size then
            let prev = nth_node (index-1)

            let newnode = ZLnkLstNode<'t>(value)
            newnode.Next <- prev.Next
            prev.Next <- newnode

            if index = m_size then
                m_rear <- newnode

            m_size <- m_size + 1
        else
            raise <| ZError(ErrorCode.OutofRange)

    member this.Remove index =
        if index >= 0 && index < m_size then
            let prev = nth_node (index-1)

            let toremove = prev.Next
            prev.Next <- toremove.Next

            if index = (m_size - 1) then
                m_rear <- prev
            m_size <- m_size - 1

            toremove.Value
        else
            raise <| ZError(ErrorCode.OutofRange)
            

    member this.Item
        with get index = 
            if index >= 0 && index < m_size then
                let node = nth_node index
                node.Value
            else
                raise <| ZError(ErrorCode.OutofRange)
        and set index value = 
            if index >= 0 && index < m_size then
                let node = nth_node index
                node.Value <- value
            else
                raise <| ZError(ErrorCode.OutofRange)

    // !!!!!!!!!! chekanote, ATTENTION: 
    // !!!!!!!!!! below method is very unsafe
    // !!!!!!!!!! after the concation, list2 still reference part of the whole list
    // !!!!!!!!!! change made on one list may reflect on the other one
    member this.ShareConcat (list2: ZLinkList<'t>) =
        m_rear.Next <- list2.PreHead.Next
        m_rear <- list2.Rear
        m_size <- m_size + list2.Size

    // #################### IEnumerable interface
    interface IEnumerable<'t> with
        member this.GetEnumerator() =
            new ZLnkLstIterator<'t>(this) :> IEnumerator<'t>

    interface IEnumerable with
        member this.GetEnumerator() =
            new ZLnkLstIterator<'t>(this) :> IEnumerator

and ZLnkLstIterator<'t>(m_list : ZLinkList<'t>) =

    // Reset() will NOT be automatically be invoked before each loop
    // so we have to set "current" position before the first element
    // as soon as the iterator is constructed (so in the constructor)
    let mutable m_current : ZLnkLstNode<'t> = m_list.PreHead

    interface IDisposable with
        member this.Dispose() = 
            ()

    interface IEnumerator<'t> with
        member this.Current =
            m_current.Value

    interface IEnumerator with
        member this.MoveNext() = 
            m_current <- m_current.Next
            m_current <> null

        member this.Reset() =
            m_current <- m_list.PreHead

        member this.Current = 
            box (this :> IEnumerator<'t>).Current








        
